/*
 * PhysicObject.cpp
 * This file is part of FunBall 
 *
 * Copyright (C) 2012 - Francois Berder
 *
 * FunBall is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * FunBall is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
 
 

#include <cassert>
#include <stdexcept>
#include "PhysicObject.hpp"
#include "PhysicWorld.hpp"


PhysicObject::PhysicObject():
m_body(),
nb(new int(0))
{
}

PhysicObject::PhysicObject(const PhysicObject& po):
m_body(po.m_body),
nb(po.nb)
{
    (*nb)++;
}
        
PhysicObject::~PhysicObject()
{
    (*nb)--;
    if(!(*nb) && m_body)
    {
        assert(GET_PHYSIC_WORLD);
        GET_PHYSIC_WORLD->DestroyBody(m_body);
        delete nb;
    }
}

PhysicObject& PhysicObject::operator=(const PhysicObject& po)
{
    m_body = po.m_body;
    nb = po.nb;
    (*nb)++;

    return *this;
}        
void PhysicObject::load(const json::Object &data)
{
    assert(GET_PHYSIC_WORLD != NULL);

    json::String &dtype = data["type"]; 
    json::Number &dangle = data["angle"];
    json::Number &dposx = data["position"]["x"];
    json::Number &dposy = data["position"]["y"];
    json::Boolean &dfixedRotation = data["fixedRotation"];
    json::Boolean &dbullet = data["bullet"];
    
    b2BodyDef bodyDef;
    if(dtype.Value() == "static")
        bodyDef.type = b2_staticBody;
    else if(dtype.Value() == "dynamic")
        bodyDef.type = b2_dynamicBody;
    else if(dtype.Value() == "kinematic")
        bodyDef.type = b2_kinematicBody;
    else
        throw std::runtime_error("Invalid data for body type.");
    
    bodyDef.angle = dangle.Value();
    bodyDef.position = b2Vec2(dposx.Value() * SFML_TO_BOX2D,dposy.Value() * SFML_TO_BOX2D);
    bodyDef.fixedRotation = dfixedRotation.Value();
    bodyDef.bullet = dbullet.Value();
    bodyDef.userData = NULL;

    m_body = GET_PHYSIC_WORLD->CreateBody(&bodyDef);

    if(dtype.Value() == "static")
    {
        json::String& dshapeType = data["shapeType"];
        if(dshapeType.Value() == "rectangle")
        {
            json::Number& dwidth = data["width"];
            json::Number& dheight = data["height"];
            b2PolygonShape* shape = new b2PolygonShape();
            shape->SetAsBox(dwidth.Value()/60.f,dheight.Value()/60.f);
            m_body->CreateFixture(shape,0);
        }
        else if(dshapeType.Value() == "square")
        {
            json::Number& dside = data["side"];
            b2PolygonShape *shape = new b2PolygonShape();
            shape->SetAsBox(dside.Value()/60.f,dside.Value()/60.f);
            m_body->CreateFixture(shape,0);
        }
        else if(dshapeType.Value() == "circle")
        {
            json::Number& dradius = data["radius"];
            b2Shape *shape = new b2CircleShape();
            shape->m_radius = dradius.Value() / 30.f;
            m_body->CreateFixture(shape,0);
        }
    }
    else
    {
        json::Array &fixtures = data["fixtures"];
        for(unsigned int i = 0; i < fixtures.Size(); ++i)
        {
            json::String& dshapeType = fixtures[i]["shapeType"];
            json::Number& dfriction = fixtures[i]["friction"];
            json::Number& drestitution = fixtures[i]["restitution"];
            json::Number& ddensity = fixtures[i]["density"];
            json::Boolean& dsensor = fixtures[i]["sensor"];
            
            b2FixtureDef fixDef;
            
            if(dshapeType.Value() == "rectangle")
            {
                json::Number& dwidth = fixtures[i]["width"];
                json::Number& dheight = fixtures[i]["height"];
                b2PolygonShape *shape = new b2PolygonShape();
                shape->SetAsBox(dwidth.Value()/(2.f*BOX2D_TO_SFML),dheight.Value()/(2.f*BOX2D_TO_SFML));
                fixDef.shape = shape;
            }
            else if(dshapeType.Value() == "square")
            {
                json::Number& dside = fixtures[i]["side"];
                b2PolygonShape *shape = new b2PolygonShape();
                shape->SetAsBox(dside.Value()/(2.f*BOX2D_TO_SFML),dside.Value()/(2.f*BOX2D_TO_SFML));
                fixDef.shape = shape;      
            }
            
            else if(dshapeType.Value() == "circle")
            {
                json::Number& dradius = fixtures[i]["radius"];
                b2CircleShape *shape = new b2CircleShape();
                shape->m_radius = dradius.Value() * SFML_TO_BOX2D;
                fixDef.shape = shape;
            }
            else
                throw std::runtime_error("Invalid shape type.");
            
            fixDef.friction = dfriction.Value();
            fixDef.restitution = drestitution.Value();
            fixDef.density = ddensity.Value();
            fixDef.isSensor = dsensor.Value();
            fixDef.userData = NULL;
            m_body->CreateFixture(&fixDef);
        }
    }
    *nb = 1;
}

sf::Vector2f PhysicObject::convertPosToSFML()
{
    assert(m_body != NULL);
    
    sf::Vector2f pos;
    pos.x = m_body->GetPosition().x * BOX2D_TO_SFML;
    pos.y = - m_body->GetPosition().y * BOX2D_TO_SFML;
    
    return pos;
}

b2Body* PhysicObject::body()
{
    return m_body;
}
